﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Media;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;

namespace psafth.RCTiming.Main
{
    class SoundPlayer
    {
        [DllImport("winmm.dll", EntryPoint = "PlaySound", SetLastError = true)]
        private extern static int PlaySound(byte[] wavData, IntPtr hModule, PlaySoundFlags flags);

        //#define SND_SYNC            0x0000  /* play synchronously (default) */
        //#define SND_ASYNC           0x0001  /* play asynchronously */
        //#define SND_NODEFAULT       0x0002  /* silence (!default) if sound not found */
        //#define SND_MEMORY          0x0004  /* pszSound points to a memory file */
        //#define SND_LOOP            0x0008  /* loop the sound until next sndPlaySound */
        //#define SND_NOSTOP          0x0010  /* don't stop any currently playing sound */

        //#define SND_NOWAIT      0x00002000L /* don't wait if the driver is busy */
        //#define SND_ALIAS       0x00010000L /* name is a registry alias */
        //#define SND_ALIAS_ID    0x00110000L /* alias is a predefined ID */
        //#define SND_FILENAME    0x00020000L /* name is file name */
        //#define SND_RESOURCE    0x00040004L /* name is resource name or atom */

        enum PlaySoundFlags
        {
            SND_SYNC = 0x0000,
            SND_ASYNC = 0x0001,
            SND_MEMORY = 0x0004
        }

        // Play a wav file appearing in a byte array
        static void Play(int index, int duration)
        {
            byte[] wav = new byte[0];

            if (index < 4)
            {
                short[] left;
                short[] right;
                CreateSamples(out left, out right, index, duration);

                wav = ConvertSamplesToWavFileFormat(left, right);
            }
            else
            {
                wav = SoundStreams[index - 4];
            }

            PlaySound(wav, System.IntPtr.Zero, PlaySoundFlags.SND_MEMORY | PlaySoundFlags.SND_SYNC);
        }

        static byte[] ConvertSamplesToWavFileFormat(short[] left, short[] right)
        {
            //Debug.Assert(left.Length == right.Length);
            const int sampleRate = 44100;
            const int channelCount = 2;
            int sampleSize = sizeof(short) * channelCount * left.Length;
            int totalSize = 12 + 24 + 8 + sampleSize;

            byte[] wav = new byte[totalSize];
            int b = 0;

            // RIFF header
            wav[b++] = (byte)'R';
            wav[b++] = (byte)'I';
            wav[b++] = (byte)'F';
            wav[b++] = (byte)'F';
            int chunkSize = totalSize - 8;
            wav[b++] = (byte)(chunkSize & 0xff);
            wav[b++] = (byte)((chunkSize >> 8) & 0xff);
            wav[b++] = (byte)((chunkSize >> 16) & 0xff);
            wav[b++] = (byte)((chunkSize >> 24) & 0xff);
            wav[b++] = (byte)'W';
            wav[b++] = (byte)'A';
            wav[b++] = (byte)'V';
            wav[b++] = (byte)'E';

            // Format header
            wav[b++] = (byte)'f';
            wav[b++] = (byte)'m';
            wav[b++] = (byte)'t';
            wav[b++] = (byte)' ';
            wav[b++] = 16;
            wav[b++] = 0;
            wav[b++] = 0;
            wav[b++] = 0; // Chunk size
            wav[b++] = 1;
            wav[b++] = 0; // Compression code
            wav[b++] = channelCount;
            wav[b++] = 0; // Number of channels
            wav[b++] = (byte)(sampleRate & 0xff);
            wav[b++] = (byte)((sampleRate >> 8) & 0xff);
            wav[b++] = (byte)((sampleRate >> 16) & 0xff);
            wav[b++] = (byte)((sampleRate >> 24) & 0xff);
            int byteRate = sampleRate * channelCount * sizeof(short); // byte rate for all channels
            wav[b++] = (byte)(byteRate & 0xff);
            wav[b++] = (byte)((byteRate >> 8) & 0xff);
            wav[b++] = (byte)((byteRate >> 16) & 0xff);
            wav[b++] = (byte)((byteRate >> 24) & 0xff);
            wav[b++] = channelCount * sizeof(short);
            wav[b++] = 0; // Block align (bytes per sample)
            wav[b++] = sizeof(short) * 8;
            wav[b++] = 0; // Bits per sample

            // Data chunk header
            wav[b++] = (byte)'d';
            wav[b++] = (byte)'a';
            wav[b++] = (byte)'t';
            wav[b++] = (byte)'a';
            wav[b++] = (byte)(sampleSize & 0xff);
            wav[b++] = (byte)((sampleSize >> 8) & 0xff);
            wav[b++] = (byte)((sampleSize >> 16) & 0xff);
            wav[b++] = (byte)((sampleSize >> 24) & 0xff);

            //Debug.Assert(b == 44);

            for (int s = 0; s != left.Length; ++s)
            {
                wav[b++] = (byte)(left[s] & 0xff);
                wav[b++] = (byte)(((ushort)left[s] >> 8) & 0xff);
                wav[b++] = (byte)(right[s] & 0xff);
                wav[b++] = (byte)(((ushort)right[s] >> 8) & 0xff);
            }

            //Debug.Assert(b == totalSize);

            return wav;
        }

        // Create a simple sine wave
        static void CreateSamples(out short[] left, out short[] right, int index, int duration)
        {
            const int sampleRate = 44100;

            int[] tones = new int[] { 640, 1000, 261, 50 };

            double frequency = tones[index];

            double multiplier = duration / 1000.0;

            int count = (int)Math.Round(sampleRate * multiplier); // Time in seconds
            left = new short[count];
            right = new short[count];

            for (int i = 0; i != count; ++i)
            {
                double t = (double)i / sampleRate; // Time of this sample in seconds
                short s = (short)Math.Floor(Math.Sin(t * 2 * Math.PI * frequency) * short.MaxValue);
                left[i] = s;
                right[i] = s;
            }
        }



        private static List<byte[]> SoundStreams = new List<byte[]>();

        internal static void LoadSound(string path)
        {
            Stream s = File.OpenRead(path);
            SoundStreams.Add(ReadFully(s));
        }

        public static byte[] ReadFully(Stream input)
        {
            byte[] buffer = new byte[16 * 1024];
            using (MemoryStream ms = new MemoryStream())
            {
                int read;
                while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
                {
                    ms.Write(buffer, 0, read);
                }
                return ms.ToArray();
            }
        }

        internal static void PlaySound(int index, int duration, bool useHorn)
        {
            try
            {
                BackgroundWorker bw = new BackgroundWorker();

                bw.WorkerReportsProgress = true;

                bw.DoWork += new DoWorkEventHandler(
                delegate(object o, DoWorkEventArgs args)
                {
                    try
                    {
                        // Call the arduinoboard to sound the horn.
                        if (useHorn)
                            ArduinoBoard.SoundHorn(duration);

                        //// Play a tone on the computer
                        //SoundPlayer p = new SoundPlayer();

                        //Stream sound = SoundStreams[index];
                        //sound.Position = 0;

                        //p.Stream = null;
                        //p.Stream = sound;
                        //p.Play();
                        //Thread.Sleep(duration);
                        //p.Stop();

                        Play(index, duration);

                    }
                    catch (Exception)
                    {
                    }
                });

                bw.RunWorkerAsync();
            }
            catch (Exception)
            {
                // TODO: Somezing.
            }
        }
    }

    enum Voice
    {
        OneFinished = 4,
        TwoFinished = 5,
        ThreeFinised = 6,
        FourFinished = 7,
        FiveFinished = 8,
        SixFinished = 9,
        SevenFinished = 10,
        EightFinished = 11,
        NineFinished = 12,
        TenFinished = 13,
        ElevenFinished = 14,
        TwelveFinished = 15
    }
}
